package com.qipa.netty;

import com.qipa.netty.handler.ServerHeartBeatHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;


/**
 * 服务的主入口，这个例子是发送http-json
 *
 * @author superzhan
 */

@Component
public final class MainServer {

    static final int PORT = 9000;

    Logger logger = Logger.getLogger(MainServer.class);

    @Autowired
    ServerHandler serverInitializer;

    public void start() throws Exception {
        // Configure the server.
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);//一个老板够了
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();   // netty服务器的创建, 辅助工具类，用于服务器通道的一系列配置
            b.option(ChannelOption.SO_BACKLOG, 1024);//服务端接受连接的队列长度，如果队列已满，客户端连接将被拒绝
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)// 设置并绑定服务端Channel,指定所使用的NIO传输的Channel
                    .handler(new LoggingHandler(LogLevel.ERROR))//日志打印
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            //第一个参数：设定IdleStateHandler心跳检测每多少秒进行一次读检测，如果多少秒内ChannelRead()方法未被调用则触发一次userEventTrigger()方法
                            //第二个参数：writerIdleTime写空闲超时时间设定，如果write()方法超过writerIdleTime时间未被调用则会触发超时事件调用userEventTrigger()方法；
                            //第三个参数：allIdleTime所有类型的空闲超时时间设定，包括读空闲和写空闲；
                            p.addLast("decoder", new StringDecoder());
                            p.addLast("encoder", new StringEncoder());
                            p.addLast(new IdleStateHandler(10, 0, 0, TimeUnit.SECONDS));//心跳配置
                            p.addLast(new ServerHeartBeatHandler());//处理心跳逻辑处理，把连接都存起来，http连接也会存起来
                            p.addLast(new ServerHandler()); /*自己写的服务器逻辑处理*/
                        }
                    });//设置子通道也就是SocketChannel的处理器， 其内部是实际业务开发的"主战场"

            //绑定并侦听某个端口
            logger.info("聊天室server启动了。");
            Channel ch = b.bind(PORT).sync().channel();

            ch.closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
